﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Diagnostics;

namespace Clarius.Silverlight.Streaming.Library.Operations
{
    internal abstract class SilverlightStreamingOperation
    {
        const int BUFFER_SIZE = 4096;
        List<String> binaryMimeType = new List<string>();

        protected AccountManager accountManager;

        internal SilverlightStreamingOperation()
        {
            binaryMimeType.Add("jpg");
            binaryMimeType.Add("jpeg");
            binaryMimeType.Add("gif");
            binaryMimeType.Add("png");
            binaryMimeType.Add("tif");
            binaryMimeType.Add("wav");
        }

        protected virtual void Initialize(AccountManager accountManager)
        {
            this.accountManager = accountManager;
        }

        protected void HandleError (Exception ex)
        {
            WebException webEx = ex as WebException;

            // it's not a WebException, meaning we most probably don't know what to do with this
            if (webEx == null)
            {
                HandleUnknownException(ex);
                return;
            }

            // it's a WebException, but no response has been received
            if (webEx.Response == null)
            {
                // most probably this is due to invalid server info or connectivity issues
                HandleErrorFromNoResponse(webEx);
                return;
            }

            // it's a WebException and we got a response back, we can try to peek at the error
            // and wrap it up in a more meaningful one
            HttpWebResponse webResponse = webEx.Response as HttpWebResponse;
            HandleErrorFromHttpStatusCode(webResponse.StatusCode, WebUtil.GetResponseErrorText(webResponse), webEx);
        }

        /// <summary>
        /// Handler for exceptions that are not WebException and most probably are NOT specific to
        /// our interaction with the Silverlight Streaming server (e.g. local machine ran out of memory)
        /// </summary>
        /// <remarks>Default behavior is to rethrow the exception assuming the user should handle
        /// non library related exceptions.
        /// </remarks>
        /// <param name="ex"></param>
        protected virtual void HandleUnknownException(Exception ex)
        {
            throw ex;
        }

        /// <summary>
        /// Handler for requests that don't cause a response to be received.
        /// </summary>
        /// <remarks>
        /// Most probably this means the server data is invalid or there is no conectivity
        /// </remarks>
        /// <param name="ex"></param>
        protected void HandleErrorFromNoResponse(WebException ex)
        {
            // it's most probably because of non existent host or conectivy issues
            if (ex.Status == WebExceptionStatus.NameResolutionFailure)
            {
                // TODO: move to SR
                throw new SilverlightStreamingException("Invalid host data or no connectivity", ex);
            }
            // it's unknown to us, just re-throw
            else
            {
                WebUtil.ThrowUnknownError(ex);
            }
        }

        /// <summary>
        /// Handler for requests that produce responses with a specific HttpStatusCode.
        /// You sohuld inspect this code and try to wrap it in a more meaningul error description
        /// based on the operation you're executing
        /// </summary>
        /// <param name="statusCode"></param>
        protected abstract void HandleErrorFromHttpStatusCode(HttpStatusCode statusCode, String errorText, Exception ex);

        protected virtual void HandleUnknownError(WebException ex)
        {
            throw new SilverlightStreamingException("Unknown error", ex);
        }

        protected abstract WebRequest GetWebRequest();

        protected virtual HttpWebResponse ExecuteWebRequest(WebRequest request)
        {
            HttpWebResponse response = null;

            try
            {
                response = request.GetResponse() as HttpWebResponse;
            }
            catch (Exception ex)
            {
                HandleError(ex);
            }

            return response;
        }

        protected virtual byte[] ExecuteWebRequestReturningBytes(WebRequest request)
        {
            HttpWebResponse response = null;

            try
            {
                response = request.GetResponse() as HttpWebResponse;
            }
            catch (Exception ex)
            {
                HandleError(ex);
            }

            
            using (Stream stream = response.GetResponseStream())
            {
            //    using (FileStream fileStream = new FileStream(,
            //FileMode.Open, FileAccess.Read))
            //    {
            //        while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
            //        {
            //            requestStream.Write(buffer, 0, bytesRead);
            //        }
            //    }
            }

            return null;
        }

        /// <summary>
        /// This method should parse the response from the Silverlight Streaming server
        /// and create corresponding typed objects if necessary.
        /// </summary>
        /// <remarks>
        /// For operations that doesn't need to return objects like DETELE this method
        /// doesn't need to be overriden.
        /// </remarks>
        /// <param name="responseText"></param>
        /// <returns></returns>
        protected virtual object ParseWebResponse(String responseText)
        {
            return null;
        }

        protected virtual object ParseWebResponse(byte[] responseBytes)
        {
            return null;
        }

        internal object Execute()
        {
            WebRequest webRequest = GetWebRequest();

            HttpWebResponse response = ExecuteWebRequest(webRequest);
            MemoryStream memoryStream;

            byte[] buffer = new byte[BUFFER_SIZE];
            byte[] finalBytes;
            if (IsBinaryContent(response.ContentType))
            {
                using (Stream responseStream = response.GetResponseStream())
                {
                    using (memoryStream = new MemoryStream())
                    {
                        int bytesRead = 0;
                        while ((bytesRead = responseStream.Read(buffer, 0, BUFFER_SIZE)) != 0)
                        {
                            memoryStream.Write(buffer, 0, bytesRead);
                        }
                        memoryStream.Position = 0;
                        finalBytes = new byte[memoryStream.Length];
                        memoryStream.Read (finalBytes, 0, finalBytes.Length);
                    }
                }
                return ParseWebResponse(finalBytes);
            }
            else
            {
                return ParseWebResponse(WebUtil.GetResponseText(response));
            }
        }

        bool IsBinaryContent(String contentType)
        {
            bool isBinary = false;
            contentType = contentType.ToLower();

            foreach (String s in binaryMimeType)
            {
                if (contentType.Contains(s))
                {
                    isBinary = true;
                    break;
                }
            }

            return isBinary;
        }
    }
}
